import re

from django.contrib.auth import authenticate
from django.core.cache import cache
from rest_framework import serializers
from django.contrib.auth.models import User
from django_redis import get_redis_connection
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

from config.dbs.redisconfig import KEY_TEMPLATE, EXPIRE_TIME
from users.models import Userdetail, Area, Address


# 序列化器 校验规则
class UserSerializer(serializers.ModelSerializer):
    # 新增验证码 uuid字段
    # 反序列化校验 前端数据
    # 允许写入
    verify = serializers.CharField(max_length=4, min_length=4, write_only=True)
    uuid = serializers.UUIDField(write_only=True)
    # userdetail表 主键 关联User  user = models.OneToOneField(User, on_delete=models.CASCADE)
    sex = serializers.IntegerField(read_only=True, source='userdetail.sex')
    phone = serializers.CharField(read_only=True, source='userdetail.phone')
    birthday = serializers.DateField(read_only=True, source='userdetail.birthday')
    avatar = serializers.CharField(read_only=True, source='userdetail.avatar')

    class Meta:
        model = User
        # 展示所有字段
        # fields = '__all__'
        # 序列化不展示 反序列化不能输入
        exclude = ['is_superuser', 'is_staff', 'is_active', 'date_joined', 'groups', 'user_permissions']
        # 密码不展示 设置反序列化时能写入
        extra_kwargs = {
            'password': {
                'write_only': True
            }
        }

    # 当客户端发送127.0.0.1:8000/users/verification/register/ 请求
    # 客户端返回验证码图片 redis 存储键值对 key: uuid value:验证码
    # 验证码校验
    def validate(self, attrs):
        verify = attrs.get('verify')
        uuid = attrs.get('uuid')
        # 验证码校验
        # 获取redis中的验证码（缓存信息）
        redis_conn = get_redis_connection(alias='verify_codes')
        # 获取redis中的验证码
        redis_verify = redis_conn.get(KEY_TEMPLATE % ("register", uuid))
        # 客户端获取 数据库得到 之后就删除数据库中的验证码信息
        cache.delete(KEY_TEMPLATE % ("register", uuid))
        if redis_verify is None:
            raise serializers.ValidationError('验证码不存在')
        # 忽略大小写
        if redis_verify.upper() != verify.upper():
            raise serializers.ValidationError('验证码错误')
        return attrs


# 登录得校验序列化器
class MyTokenObtainSerializer(TokenObtainPairSerializer):
    # 有没有对应得模型表 模型表有无字段 登陆时 通过客户端传入验证码和uuid
    verify = serializers.CharField(max_length=4, min_length=4, write_only=True)
    uuid = serializers.UUIDField(write_only=True)

    # TokenObtainPairSerializer 默认校验用户名和密码 返回token
    # 在这个基础上 检验验证码 uuid 重写父类方法
    # 对象级别检验 客户端传入的都在 attrs{uuid:"",verify:"",username:"",password:""}
    def validate(self, attrs):
        verify = attrs.get('verify')
        uuid = attrs.get('uuid')
        username = attrs.get('username')
        password = attrs.get('password')

        # 获取数据库数据
        redis_conn = get_redis_connection(alias='verify_codes')
        # 获取redis中的验证码
        redis_verify = redis_conn.get(KEY_TEMPLATE % ("login", uuid))
        # 客户端获取 数据库得到 之后就删除数据库中的验证码信息
        cache.delete(KEY_TEMPLATE % ("login", uuid))
        # 判断redis中 是否存在这个键值对
        if redis_verify is None:
            raise serializers.ValidationError('验证码不存在')
        # 忽略大小写
        if redis_verify.upper() != verify.upper():
            raise serializers.ValidationError('验证码错误')

        # 校验用户名和密码 数据库=?=客户端
        user = authenticate(username=username, password=password)
        if not user:
            raise serializers.ValidationError('用户名或密码错误')
        # 校验成功 token 令牌
        refresh = self.get_token(user)
        # 指定数据返回
        return {"user": user.username, "token": str(refresh.access_token)}


# 用户详情序列化器
class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Userdetail
        # fields = '__all__'
        exclude = ['is_delete']


# 修改密码的序列化器
class UpdatePasswordSerializer(serializers.Serializer):
    # 序列化的时候 客户端能看到的字段
    password = serializers.CharField(max_length=128, write_only=True, label='原密码')
    new_password = serializers.CharField(max_length=128, write_only=True, label='新密码')
    re_new_password = serializers.CharField(max_length=128, write_only=True, label='确认密码')

    # 对象级别的校验 attrs {password:"",new_password:"",re_password:""}
    def validate(self, attrs):
        if attrs.get('new_password') != attrs.get('re_new_password'):
            raise serializers.ValidationError('两次密码不一致')
        if attrs.get('password') == attrs.get('new_password'):
            raise serializers.ValidationError('新密码不能与原密码一致')
        # 特殊情况 最开始 输入 就错误 校验原密码和数据库密码是否一致 user.check_password(password)
        user = self.context['request'].user
        if not user.check_password(attrs.get('password')):
            raise serializers.ValidationError('原密码错误')
        return attrs


# 地址序列化器
class AreaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Area
        fields = ['id', 'name']


# 省份下的子级序列化器
class AreaChildSerializer(serializers.ModelSerializer):
    # 嵌套序列化器
    area_set = AreaSerializer(many=True, read_only=True)

    class Meta:
        model = Area
        fields = ['id', 'name', 'area_set']


# class AreaSerializer(serializers.ModelSerializer):
#     # 嵌套序列化器
#     area_set = serializers.SerializerMethodField()
#
#     class Meta:
#         model = Area
#         fields = ['id', 'name', 'area_set']
#
#     def get_area_set(self, obj):
#         # 动态获取子级区域数据
#         queryset = obj.area_set.all()
#         return AreaSerializer(queryset, many=True).data


# 详情地址序列化器
class AddressSerializer(serializers.ModelSerializer):
    province_name = serializers.CharField(source="province.name", read_only=True)
    city_name = serializers.CharField(source="city.name", read_only=True)
    district_name = serializers.CharField(source="district.name", read_only=True)

    class Meta:
        model = Address
        # fields = "__all__"
        exclude = ["is_delete"]

    # 手机号怎么校验
    # def validate_mobile(self, value):
    #     if not re.match(r"1[3-9]\d{9}", value):
    #         raise serializers.ValidationError("手机号格式不符")
    #     return value
